class Poppet {
    private int i;

    Poppet(int i) {
        this.i = i;
    }
}

public class FinalBlank {
    private final int i = 0;
    private final int j;  // 空白 final
    private final Poppet p;  // 空白 final 引用

    // 空白 final 字段必须在构造函数中初始化
    public FinalBlank() {
        j = 1;
        p = new Poppet(1);
    }

    public FinalBlank(int x) {
        j = x;
        p = new Poppet(x);
    }

    @Override
    public String toString() {
        return i + " " + j + " " + p;
    }

    public static void main(String[] args) {
        FinalBlank fb = new FinalBlank();
        System.out.println(fb);
        FinalBlank fb2 = new FinalBlank(16);
        System.out.println(fb2);
    }
}

/*
对final执行赋值的操作只能发生在两个地方：
要么在字段定义处使用表达式进行赋值，要么在每个构造器中。
这保证了final字段在使用前总是被初始化。
 */